package ws

import (
	"sync"
	"time"

	"github.com/gogf/gf/container/garray"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"live/app/model/px_user"

	"github.com/gorilla/websocket"
)

// websocket连接信息
type Client struct {
	RoomId             int             // 所在房间id 0未加入任何房间
	User               *px_user.Entity  // 用户信息
	Request            *ghttp.Request   // 用户 Request，用户登录以后才有
	Socket             *ghttp.WebSocket // 用户连接
	HeartbeatFailTimes int              // 心跳失败次数
	SendLock           sync.Mutex       // 避免并发 发送数据到客户端时,一个任务关闭了另一个任务的发送 io.write
}

// SendMsg 发送文本消息给客户端
func (c *Client) SendMsg(message string) {
	c.SendLock.Lock()
	defer c.SendLock.Unlock()
	err := c.Socket.WriteMessage(websocket.TextMessage, []byte(message))
	if err != nil {
		g.Log().Error("[websocket]", err)
	}
}

// SendJson 发送Json消息给客户端
func (c *Client) SendJson(data interface{}) error {
	c.SendLock.Lock()
	defer c.SendLock.Unlock()
	err := c.Socket.WriteJSON(data)
	if err != nil {
		g.Log().Error("[websocket]", err)
	}
	return err
}

// Close 关闭连接
func (c *Client) Close() {
	err := c.Socket.Close()
	if err != nil {
		g.Log().Error("[websocket]关闭连接错误", err.Error())
	}
}

// Heartbeat 心跳检查(服务端主动心跳)
func (c *Client) Heartbeat() {
	ticker := time.NewTicker(time.Second * 50) // 间隔50秒心跳一次
	defer func() {
		ticker.Stop() // 停止该client的心跳检测
		_ = c.Socket.Close()
	}()
	for {
		<-ticker.C
		if err := c.Socket.WriteMessage(websocket.PingMessage, []byte("ping")); err != nil {
			c.HeartbeatFailTimes++
			if c.HeartbeatFailTimes > 4 { // 失败4次断开连接
				return
			}
		} else if c.HeartbeatFailTimes > 0 {
			c.HeartbeatFailTimes--
		}
	}
}

// ReturnJson 记录日志并返回结果
func (c *Client) ReturnJson(out *JsonData) error {
	if value, ok := out.Data.(*OutList); ok {
		if g.IsEmpty(value.List) {
			value.List = garray.NewArray()
			out.Data = value
		}
	}
	err := c.SendJson(out)
	if err != nil {
		out.LogTitle = "ws发送失败"
	}
	// 写日志
	return err
}

// Success 返回成功
func (c *Client) Success(out *JsonData) error {
	out.Code = 0
	return c.ReturnJson(out)
}

// Error 返回错误
func (c *Client) Error(out *JsonData) error {
	out.Code = 1
	return c.ReturnJson(out)
}

func (c *Client) PushList(list *OutList, cmd string, callback ...string) error {
	out := new(JsonData)
	out.Code = 0
	out.Data = list
	out.Cmd = cmd
	if len(callback) > 0 {
		out.CallBack = callback[0]
	}
	return c.ReturnJson(out)
}
